﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using System.Xml;
using System.Reflection;
using Microsoft.Xna.Framework.Content.Pipeline.Serialization.Intermediate;
using Microsoft.Xna.Framework.Content.Pipeline.Serialization.Compiler;
using Microsoft.Xna.Framework.Content.Pipeline;

/**
 * The goal for the level editing workflow is that saved maps will go directly
 * into the content pipeline, without having to recompile the project.  This
 * makes it easier for the artist to edit levels (it would be odd to require
 * the artist to understand how visual studio projects work).
 * 
 * Therefore, the goal of the save manager is to produce two output formats
 * for the map it saves: one in xml and one in xnb.  The two will get saved
 * in separate folders.
 * 
 * We want the xnb file so that we can hot load it into the program whenever.
 * On the other hand, we want the xml format for human readable form, in case
 * there are bugs, errors, etc.
 * 
 * XNA makes it difficult to compile things into xnb format on the fly.  That is
 * why I approach this in a roundabout fashion through reflection.  Thus, this
 * is unstable and may become useless in future XNA versions.  But for now, this
 * should work!
 */
namespace MapLibrary
{
    class SaveManager
    {
        /**
         * Root directory is the /bin path.  Thus
         * we need to get to /bin/Content/xml.
         */
        private const string XML_DIRECTORY = "./Content/xml";

        /**
         * Root directory is the /bin path. Thus we need to
         * get to /bin/Content/xnb
         */
        private const string XNB_DIRECTORY = "./Content/xnb";

        /**
         * 
         * Saves the map into the two file formats
         */
        public void Save(Level pLevel, Map pMap)
        {
            SaveXNB(pMap);
            SaveXML(pLevel, pMap);
        }

        /**
         * Saves the xnb file.
         */
        private void SaveXNB(Map pMap)
        {
            ConstructorInfo[] constructors = typeof(ContentCompiler).GetConstructors(
                BindingFlags.NonPublic | BindingFlags.Instance);

            MethodInfo addWriter = typeof(ContentCompiler).GetMethod(
                "AddTypeWriter",
                BindingFlags.NonPublic | BindingFlags.Instance);

            MethodInfo compileContent = typeof(ContentCompiler).GetMethod(
                "Compile",
                BindingFlags.NonPublic | BindingFlags.Instance);

            ContentCompiler compiler = constructors[0].Invoke(null) as ContentCompiler;

            if (!Directory.Exists(XNB_DIRECTORY))
            {
                Directory.CreateDirectory(XNB_DIRECTORY);
            }

            addWriter.Invoke(compiler, new object[] { new MapTypeWriter()});

            using (FileStream stream = new FileStream(
                XNB_DIRECTORY + "\\" + pMap.Name + ".xnb",
                FileMode.Create))
            {
                compileContent.Invoke(
                    compiler,
                    new object[]
                {
                    stream,
                    pMap,
                    TargetPlatform.Windows,
                    GraphicsProfile.HiDef,
                    true,
                    XNB_DIRECTORY,
                    XNB_DIRECTORY
                });
            }
        }

        /**
         * Saves the xml file.
         */
        private void SaveXML(Level pLevel, Map pMap)
        {
            //first check if xml folder exists
            if (!Directory.Exists(XML_DIRECTORY))
            {
                Directory.CreateDirectory(XML_DIRECTORY);
            }

            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Indent = true;

            using (XmlWriter writer = XmlWriter.Create(XML_DIRECTORY + "\\" + pMap.Name + ".xml", settings))
            {
                IntermediateSerializer.Serialize<Map>(writer, pMap, null);
            }

            using (XmlWriter writer = XmlWriter.Create(XML_DIRECTORY + "\\" + "testing" + ".xml", settings))
            {
                IntermediateSerializer.Serialize<Level>(writer, pLevel, null);
            }
        }
    }
}
